package genesis.familytree.server.common.code.builder;

import genesis.familytree.server.common.code.*;
import genesis.familytree.server.common.exception.AttributeValueNotFoundException;
import genesis.familytree.server.common.exception.ExpressionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;

/**
 *
 */
public abstract class AbstractDataTypeBuilder {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractDataTypeBuilder.class);

    protected Object getValue(String name, Map<String, Object> params) throws ExpressionException {
        if (name.contains(".")) {
            String[] names = name.split("\\.");
            String objName = names[0];

            Object obj;
            if (params.containsKey(objName)) {
                obj = params.get(objName);

                if (null != obj) {
                    for (int i = 1; i < names.length; i++) {
                        String propName = names[i];
                        obj = getValue(obj, propName);
                        if (null == obj) {
                            break;
                        }
                    }
                }else {
                    obj = params.get(name);
                }
            }else if (params.containsKey(name)) {
                obj = params.get(name);
            }else {
                throw new AttributeValueNotFoundException("name: " + name + " value not found ");
            }

            return obj;
        }else if (params.containsKey(name)) {
            return params.get(name);
        }else {
            throw new AttributeValueNotFoundException("name: " + name + " value not found ");
        }
    }

    private Object getValue(Object obj, String propertyName) {
        if (obj instanceof Map) {
            return ((Map) obj).get(propertyName);
        }

        Class<?> clazz = obj.getClass();
        try {
            String propName = propertyName;
            char c = propName.charAt(0);
            if (Character.isLowerCase(c)) {
                c = Character.toUpperCase(c);
                propName = c + propName.substring(1, propName.length());
            }

            String methodName = "get" + propName;
            try { //查找 getter
                Method method = clazz.getMethod(methodName);
                if (LOG.isDebugEnabled()) {
                    LOG.debug("class[" + clazz.getName() + "] method (" + methodName + "() ) found");
                }
                return method.invoke(obj);
            } catch (NoSuchMethodException ex) {
                LOG.debug("class[" + clazz.getName() + "] method (" + methodName + "() ) not found");
            }

            methodName = "is" + propName;
            try { //查找 is getter(boolean)
                Method method = clazz.getMethod(methodName);
                if (LOG.isDebugEnabled()) {
                    LOG.debug("class[" + clazz.getName() + "] method (" + methodName + "() ) found");
                }
                return method.invoke(obj);
            } catch (NoSuchMethodException ex) {
                LOG.debug("class[" + clazz.getName() + "] method (" + methodName + "() ) not found");
            }
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }

        try { // 查找field
            Field declaredField = clazz.getDeclaredField(propertyName);
            if (LOG.isDebugEnabled()) {
                LOG.debug("class[" + clazz.getName() + "] field (" + propertyName + ") found");
            }

            declaredField.setAccessible(true);
            return declaredField.get(obj);
        } catch (NoSuchFieldException ex) {
            LOG.debug("class[" + clazz.getName() + "] field (" + propertyName + ") not found");
        } catch (Exception ex) {
            LOG.error(ex.getMessage(), ex);
        }

        try { // 查找method
            Method method = clazz.getMethod(propertyName);

            if (LOG.isDebugEnabled()) {
                LOG.debug("class[" + clazz.getName() + "] method (" + propertyName + "() ) found");
            }

            return method.invoke(obj);
        } catch (NoSuchMethodException ex) {
            LOG.debug("class[" + clazz.getName() + "] method (" + propertyName + "() ) not found");
        } catch (Exception ex) {
            LOG.error(ex.getMessage(), ex);
        }

        return null;
    }
}
